package simpledb.execution;

import simpledb.common.DbException;
import simpledb.common.Type;
import simpledb.storage.Tuple;
import simpledb.storage.TupleDesc;
import simpledb.transaction.TransactionAbortedException;

import java.util.List;
import java.util.NoSuchElementException;

/**
 * Project 是一个执行算子，用于实现关系代数中的投影操作。
 * 它从子算子中读取元组（Tuple），并根据指定字段构造一个新的元组作为输出。
 */
public class Project extends Operator {

    private static final long   serialVersionUID = 1L;

    // 子算子，用于获取原始元组数据
    private OpIterator          child;

    // 投影后的元组结构描述
    private final TupleDesc     td;

    // 要保留的字段在原元组中的索引列表
    private final List<Integer> outFieldIds;

    /**
     * 构造函数接受一个子算子和要输出的字段列表
     *
     * @param fieldList 输出字段在原元组中的索引列表
     * @param typesList 输出元组各字段的类型列表
     * @param child     子算子，用于获取原始元组
     */
    public Project(List<Integer> fieldList, List<Type> typesList, OpIterator child) {
        this(fieldList, typesList.toArray(new Type[] {}), child);
    }

    /**
     * 使用字段索引列表、字段类型数组和子算子构造 Project 算子
     */
    public Project(List<Integer> fieldList, Type[] types, OpIterator child) {
        this.child = child;
        outFieldIds = fieldList;
        String[] fieldAr = new String[fieldList.size()];
        TupleDesc childtd = child.getTupleDesc();

        // 根据原元组字段索引获取对应的字段名
        for (int i = 0; i < fieldAr.length; i++) {
            fieldAr[i] = childtd.getFieldName(fieldList.get(i));
        }

        // 创建新的元组结构描述
        td = new TupleDesc(types, fieldAr);
    }

    /**
     * 获取该算子输出的元组结构描述
     */
    public TupleDesc getTupleDesc() {
        return td;
    }

    /**
     * 打开该算子及其子算子
     */
    public void open() throws DbException, NoSuchElementException, TransactionAbortedException {
        child.open();
        super.open();
    }

    /**
     * 关闭该算子及其子算子
     */
    public void close() {
        super.close();
        child.close();
    }

    /**
     * 重置子算子，以便重新遍历元组
     */
    public void rewind() throws DbException, TransactionAbortedException {
        child.rewind();
    }

    /**
     * fetchNext 的具体实现。从子算子中获取下一个元组，
     * 并从中提取出需要输出的字段，构建新的元组返回。
     *
     * @return 下一个经过投影处理的元组；若无更多元组则返回 null
     */
    protected Tuple fetchNext() throws NoSuchElementException, TransactionAbortedException, DbException {
        if (!child.hasNext())
            return null;

        Tuple t = child.next();
        Tuple newTuple = new Tuple(td); // 新建一个符合新结构的元组

        // 设置记录ID（保持一致性）
        newTuple.setRecordId(t.getRecordId());

        // 按照 outFieldIds 列表中的索引顺序复制字段值到新元组
        for (int i = 0; i < td.numFields(); i++) {
            newTuple.setField(i, t.getField(outFieldIds.get(i)));
        }

        return newTuple;
    }

    /**
     * 获取当前算子的子算子数组
     */
    @Override
    public OpIterator[] getChildren() {
        return new OpIterator[] { this.child };
    }

    /**
     * 设置当前算子的子算子
     */
    @Override
    public void setChildren(OpIterator[] children) {
        if (this.child != children[0]) {
            this.child = children[0];
        }
    }
}
